package com.ms.service.module.analysis;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.ms.game.common.GameType;
import com.ms.game.gmtool.message.SGmtoolGetRealTimePlayerListData;
import com.ms.game.slot.base.GainRateData;
import com.ms.game.slot.base.GainRateSystem;
import com.ms.player.Player;
import com.ms.player.PlayerUserType;
import com.ms.service.module.analysis.async.InitRealTimeAnalysisAsync;
import com.ms.service.module.analysis.async.SaveClientReportAsync;
import com.ms.service.module.analysis.async.UpdateRealTimeMemberAsync;
import com.ms.service.module.analysis.async.UpdateRealTimeTotalBetAsync;
import com.ms.service.module.analysis.db.AnalysisDAO;
import com.ms.service.module.analysis.db.model.ClientReportModel;
import com.ms.service.module.analysis.db.model.PlayerEnterSeatLogModel;
import com.ms.service.module.analysis.db.model.RealTimeAnalysisModel;
import com.ms.service.module.analysis.message.ClientReportData;
import com.ms.service.module.analysis.message.MachineData;
import com.ms.service.module.analysis.message.PlayerBetData;
import com.ms.service.module.analysis.message.PlayerEnterSeatData;
import com.ms.service.module.analysis.message.SRealTimePlayerCountRequest;
import com.ms.service.module.analysis.message.SRealTimePlayerCountResponse;
import com.xpec.c4.game.common.Helper;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.group.transportlayer.GroupMessage;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.ModuleName;


public class AnalysisProcessor {
	/** log object */
	private static final Log log = LogFactory.getLog(AnalysisProcessor.class);
	
	private AnalysisModule module = null;

	public AnalysisProcessor(){
		
	}
	
	private static class SingletonHolder{
		private static AnalysisProcessor instance = new AnalysisProcessor();
	}
	
	public static AnalysisProcessor getInstance (){
		return SingletonHolder.instance;
	}
	
	/*
	 * 即時玩家人數紀錄
	 * 平台 -遊戲館類型  -人數
	 */
	public HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, Integer>>> realTimeMemberCountMap
	= new HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, Integer>>>();

	/**
	 * 即時玩家與機台狀態紀錄
	 */
	public HashMap<String, PlayerSeatData> realTimePlayerWithMachineMap = new HashMap<String, PlayerSeatData>();
	
	/*
	 * 即時玩家"總"投注紀錄
	 * 平台 -遊戲館類型  - 總投注
	 */
	public HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>> totalBetMap
	= new HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>>();
	
	/**
	 * 即時玩家投注紀錄
	 * 平台 -遊戲館類型  - 投注
	 */
	public HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>> realTimeTotalBetMap
	= new HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>>();

	/**
	 * 取得即時玩家與機台狀態紀錄
	 */
	public HashMap<String, PlayerSeatData> getRealTimePlayerWithMachineMap() {
		return realTimePlayerWithMachineMap;
	}

	/**
	 * 新的即時統計
	 */
	private RealTimeAnalysisManager realTimeAnalysisManager = new RealTimeAnalysisManager();
	public void initRealTimeManager(AnalysisModule module){
		if(this.realTimeAnalysisManager != null){
			this.realTimeAnalysisManager.init(module);
		}
	}
	
	/**
	 * 送玩家出入座狀態寫紀錄，同時處理即時在線人數變動
	 * @param playerNameWithNameNumbr
	 * @param guid
	 * @param platformTypeString 玩家的平台字串 
	 * @param gameName {@link GameType}
	 * @param hallType 館別
	 * @param machineId 機台號碼
	 * @param playerEnterSeatStatusType {@link PlayerEnterSeatStatusType}, 
	 * 	如果是 {@link PlayerEnterSeatStatusType#ENTER_SEAT_REQUEST} 
	 * 	則不會參考  playerUserType, isTrialAccount, isUseTrialCoin
	 * @param playerUserType {@link PlayerUserType}
	 * @param isTrialAccount 玩家是否立即玩帳號
	 * @param isUseTrialCoin 玩家是否使用體驗幣
	 */
	public static void sendPlayerEnterSeat(String playerNameWithNameNumbr,
									String guid,
									String platformTypeString,
									GameType gameName,
									String hallType,
									int machineId,
									int gainRateID,
									PlayerEnterSeatStatusType playerEnterSeatStatusType,
									int playerUserType,
									boolean isTrialAccount,
									boolean isUseTrialCoin
									){

		MachineData machineData = new MachineData();
		machineData.setGameType(gameName.name());
		machineData.setHallType(hallType);
		machineData.setMachineId(String.valueOf(machineId));
		machineData.setGainRateID(gainRateID);
		
		PlayerEnterSeatData data = new PlayerEnterSeatData();
		data.setPlayerNameWithNumber(playerNameWithNameNumbr);
		data.setPlayerId(guid);
		data.setMachineData(machineData);
		data.setStatus(playerEnterSeatStatusType.getStatus());
		data.setPlatformType(platformTypeString);
		data.setPlayerUserType(playerUserType);
		data.setTrialAccount(isTrialAccount);
		data.setUseTrialCoin(isUseTrialCoin);
		
		GroupMessage msg = new GroupMessage();
		msg.setDestModuleID(ModuleName.ANALYSIS);
		msg.setMessageType(MessageTypes.S_ANALYSIS_PLAYER_ENTER_SEAT);
		msg.setMessageContent(data);
		MessageAction.sendMessage(msg);		
	}
	
	/**
	 * 專門送大廳的玩家出入座狀態給統計
	 * @param playerNameWithNameNumbr
	 * @param guid
	 * @param platformTypeString
	 * @param playerEnterSeatStatusType
	 * @param playerUserType
	 * @param isTrialAccount
	 * @param isUseTrialCoin
	 */
	public static void sendPlayerEnterLobby(Player player, PlayerEnterSeatStatusType playerEnterSeatStatusType){

		PlayerEnterSeatData data = new PlayerEnterSeatData();
		data.setPlayerNameWithNumber(player.getNameWithNumber());
		data.setPlayerId(player.getGuid().toString());
		data.setMachineData(null);
		data.setStatus(playerEnterSeatStatusType.getStatus());
		data.setPlatformType(player.getLastLoginPlatform());
		data.setPlayerUserType(player.getUserType());
		data.setTrialAccount(player.isTrialAccount());
		data.setUseTrialCoin(player.isUseTrialCoin());
		
		GroupMessage msg = new GroupMessage();
		msg.setDestModuleID(ModuleName.ANALYSIS);
		msg.setMessageType(MessageTypes.S_ANALYSIS_PLAYER_ENTER_SEAT);
		msg.setMessageContent(data);
		MessageAction.sendMessage(msg);		
	}
	
	/**
	 * (1)處理玩家出入機台寫入LOG&DB
	 * (2)如果是玩家就處理即時人數變動
	 */
	public void PlayerEnterSeat(PlayerEnterSeatData data){
		
		//處理儲存出入座紀錄
		savePlayerEnterSeat(
				data.getPlayerNameWithNumber(),
				data.getPlayerId(),
				data.getMachineData(),
				data.getStatus());
				
		//如果是真人玩家就處理realTimePlayerWithMachineMap的更動
		//在送入座要求的時候並不知道是否是真人玩家，因此PlayerUserType都會以0傳入；且入座要求並不會在統計總人數裡增加所以就直接return
		if(data.getStatus() == PlayerEnterSeatStatusType.ENTER_SEAT_REQUEST.getStatus())
			return;
		if(data.getPlayerUserType() == PlayerUserType.PLAYER){
			
			//判斷是否合法的platformString
			PlatformType platformType = PlatformType.getENUMbyString(data.getPlatformType());
			if(platformType == null)
				return;
			
			//判斷是否合法的GameNameWithHallType
			GameNameWithHallType gameNameWithHallType;
			String gainRateStr = BigDecimal.ONE.toString();
			if(data.getMachineData() == null){
				gameNameWithHallType = GameNameWithHallType.LOBBY;
			}
			else{
				String gameType = data.getMachineData().getGameType();
				String hallType = data.getMachineData().getHallType();
				int gainRateID = data.getMachineData().getGainRateID();
				gameNameWithHallType = GameNameWithHallType.getENUMbyString(gameType, hallType);
				
				GainRateData gainRateData = GainRateSystem.getGainRateData(gameType, hallType, gainRateID);
				if(GainRateSystem.isAffectGame(gameType) && gainRateData != null){
					gainRateStr = gainRateData.getGainRate().toString();
				}
				
			}
			if(gameNameWithHallType == null)
				return;
			
			//如果是入座成功或開始離線掛機就直接PUT進去(會刷掉原本的紀錄)
			if(data.getStatus() == PlayerEnterSeatStatusType.ENTER_SEAT_SUCCESS.getStatus() ||
			   data.getStatus() ==  PlayerEnterSeatStatusType.OFFLINE_SPIN.getStatus()){
				
				PlayerSeatData playerSeatData = new PlayerSeatData();
				playerSeatData.setGameNameWithHallType(gameNameWithHallType);
				playerSeatData.setGainRateStr(gainRateStr);
				playerSeatData.setPlatformType(platformType);
				//預設最新一注為0
				playerSeatData.setLastBet(BigDecimal.ZERO);
				playerSeatData.setMemberType(MemberType.FORMAL_MEMBER);
				
				if(data.getStatus() == PlayerEnterSeatStatusType.ENTER_SEAT_SUCCESS.getStatus()){
					playerSeatData.setPlayerEnterSeatStatusType(PlayerEnterSeatStatusType.ENTER_SEAT_SUCCESS);
				}
				if(data.getStatus() == PlayerEnterSeatStatusType.OFFLINE_SPIN.getStatus()){
					playerSeatData.setPlayerEnterSeatStatusType(PlayerEnterSeatStatusType.OFFLINE_SPIN);
					playerSeatData.setMemberType(MemberType.OFFLINE_SPIN_MEMBER);
				}
				
				if(data.isUseTrialCoin() && !data.isTrialAccount())
					playerSeatData.setMemberType(MemberType.TRAIL_MEMBER);
				if(data.isTrialAccount())
					playerSeatData.setMemberType(MemberType.NO_ACCOUNT);

				//如果原本已經有資料的，先做原本資料的移除(扣人數)
				if(realTimePlayerWithMachineMap.containsKey(data.getPlayerId())){
					PlayerSeatData removeData = realTimePlayerWithMachineMap.get(data.getPlayerId());
					realTimeAnalysisManager.updateBySeatData(RealTimeAnalysisManager.REMOVE, removeData);
				}
				
				realTimeAnalysisManager.updateBySeatData(RealTimeAnalysisManager.ADD, playerSeatData);
				realTimePlayerWithMachineMap.put(data.getPlayerId(), playerSeatData);
			}
			//如果是離座、離線保留、結束離線掛機，就把玩家移除
			else if (data.getStatus() == PlayerEnterSeatStatusType.LEAVE_SEAT.getStatus() ||
					 data.getStatus() == PlayerEnterSeatStatusType.OFFLINE_KEEP_SEAT.getStatus() ||
					 data.getStatus() == PlayerEnterSeatStatusType.LEAVE_OFFLINE_SPIN.getStatus()) {
				//移除玩家
				PlayerSeatData seatData = realTimePlayerWithMachineMap.remove(data.getPlayerId());
				realTimeAnalysisManager.updateBySeatData(RealTimeAnalysisManager.REMOVE, seatData);
			}
			//其餘的狀態不需要紀錄
			else {
				return;
			}
			//座位狀態更改刷新
			saveRealTimePlayerWithMachineMapChangeResult(false);
		}
	}
		
	/**
	 * 把realTimePlayerWithMachineMap整份統計一次
	 * 再處理更新realTimeMemberCountMap和儲存到DB
	 */
	public void saveRealTimePlayerWithMachineMapChangeResult(boolean isInit){
		HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, Integer>>> tmpRealTimeMemberCountMap
		= new HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType,Integer>>>();

		//把tmp建一個全部是0的完整MAP，以免下面GET不到
		for(PlatformType platformType : PlatformType.values()){
			tmpRealTimeMemberCountMap.put(platformType, new HashMap<GameNameWithHallType, HashMap<MemberType, Integer>>());
			for(GameNameWithHallType gameNameWithHallType : GameNameWithHallType.values()){
				tmpRealTimeMemberCountMap.get(platformType).put(gameNameWithHallType, new HashMap<MemberType, Integer>());
				for(MemberType memberType : MemberType.values()){
					tmpRealTimeMemberCountMap.get(platformType).get(gameNameWithHallType).put(memberType, 0);
				}
			}
		}
		
		//把realTimePlayerWithMachineMap人數加進tmp
		for(String guidsString : realTimePlayerWithMachineMap.keySet()){
			PlayerSeatData playerSeatData = realTimePlayerWithMachineMap.get(guidsString);
			
			switch (playerSeatData.getPlayerEnterSeatStatusType()) {
			case ENTER_SEAT_SUCCESS:{
				int count = tmpRealTimeMemberCountMap.get(playerSeatData.getPlatformType()).get(playerSeatData.getGameNameWithHallType()).get(playerSeatData.getMemberType());
				count ++;
				tmpRealTimeMemberCountMap.get(playerSeatData.getPlatformType()).get(playerSeatData.getGameNameWithHallType()).put(playerSeatData.getMemberType(), count);
				break;
			}
			case OFFLINE_SPIN:{
				int count = tmpRealTimeMemberCountMap.get(playerSeatData.getPlatformType()).get(playerSeatData.getGameNameWithHallType()).get(playerSeatData.getMemberType());
				count ++;
				tmpRealTimeMemberCountMap.get(playerSeatData.getPlatformType()).get(playerSeatData.getGameNameWithHallType()).put(playerSeatData.getMemberType(), count);
				break;
			}
			default:{
				//因為realTimePlayerWithMachineMap只有在ENTER_SEAT_SUCCESS和OFFLINE_SPIN時會置入
				if (log.isErrorEnabled()) {
					log.error("something wrong at putting record in realTimePlayerWithMachineMap, tmpEnterSeatStatusType="
							+ playerSeatData.getPlayerEnterSeatStatusType().name());
				}
				break;
			}
			}
		}
		
		//檢查整個tmpMAP和realTimeMemberCountMap有沒有不同
		for(PlatformType platformType : PlatformType.values()){
			for(GameNameWithHallType gameNameWithHallType : GameNameWithHallType.values()){
				for(MemberType memberType : MemberType.values()){
					//如果tmp與紀錄相左，就更新紀錄並寫DB
					//另外，判斷是否是剛開機的狀態，如果是剛開機就強制寫一次所有的紀錄
					if(!tmpRealTimeMemberCountMap.get(platformType).get(gameNameWithHallType).get(memberType)
					    .equals(realTimeMemberCountMap.get(platformType).get(gameNameWithHallType).get(memberType))
					    || isInit){
					    //更新紀錄
						realTimeMemberCountMap.get(platformType).get(gameNameWithHallType)
					    .put(memberType, tmpRealTimeMemberCountMap.get(platformType).get(gameNameWithHallType).get(memberType));
						
					    //寫DB
						RealTimeAnalysisModel realTimeAnalysisModel = new RealTimeAnalysisModel();
						realTimeAnalysisModel.setPlatformType(platformType);
						realTimeAnalysisModel.setGameNameWithHallType(gameNameWithHallType);
						
						switch (memberType) {
						case FORMAL_MEMBER:{
							realTimeAnalysisModel.setRealTimeMemberFormal(realTimeMemberCountMap.get(platformType).get(gameNameWithHallType).get(MemberType.FORMAL_MEMBER));
							break;
						}
						case OFFLINE_SPIN_MEMBER:{
							realTimeAnalysisModel.setRealTimeMemberOfflineSpin(realTimeMemberCountMap.get(platformType).get(gameNameWithHallType).get(MemberType.OFFLINE_SPIN_MEMBER));
							break;
						}
						case TRAIL_MEMBER:{
							realTimeAnalysisModel.setRealTimeMemberTrial(realTimeMemberCountMap.get(platformType).get(gameNameWithHallType).get(MemberType.TRAIL_MEMBER));
							break;
						}
						case NO_ACCOUNT:{
							realTimeAnalysisModel.setRealTimeMemberNoAccount(realTimeMemberCountMap.get(platformType).get(gameNameWithHallType).get(MemberType.NO_ACCOUNT));
							break;
						}
						default:
							break;
						}
						
						UpdateRealTimeMemberAsync saveData = new UpdateRealTimeMemberAsync();
						saveData.setModel(realTimeAnalysisModel);
						saveData.setMemberType(memberType);
						module.addAsyncCommand(saveData);
					}
					else continue;
				}
			}
		}	
	}
	
	/*
	 * 儲存玩家出入機台LOG
	 */
	public void savePlayerEnterSeat(String playerNameWithNumber, String playerId, MachineData machine, int status){
		PlayerEnterSeatLogModel playerEnterSeatLogModel = new PlayerEnterSeatLogModel();
		playerEnterSeatLogModel.setPlayerName(playerNameWithNumber); 
		playerEnterSeatLogModel.setPlayerId(playerId);
		playerEnterSeatLogModel.setStatus(status);
		
		if(machine != null){
			playerEnterSeatLogModel.setGameType(machine.getGameType());
			playerEnterSeatLogModel.setHallType(machine.getHallType());
			playerEnterSeatLogModel.setMachinId(machine.getMachineId());
		}
		else{
			playerEnterSeatLogModel.setGameType(GameNameWithHallType.LOBBY.name());
		}
		
		playerEnterSeatLogModel.setEventDate(GameUtility.longToDate(System.currentTimeMillis()));
		module.saveDbLog(playerEnterSeatLogModel);
	}
	
	/**
	 * 處理玩家即時下注紀錄
	 * @param data
	 */
	public void PlayerBet(PlayerBetData data){
		//判斷是否合法的platformString
		PlatformType platformType = PlatformType.getENUMbyString(data.getPlatform());
		if(platformType == null)
			return;
		
		//判斷是否合法的gameNameWithHallType
		GameNameWithHallType gameNameWithHallType = GameNameWithHallType.getENUMbyString(data.getGameName(), data.getHallType());
		if(gameNameWithHallType == null)
			return;
		
		//為了不讓DB每次都要寫全部的MEMBER紀錄，在DAO有做個別寫入的方法
		MemberType memberTypeforDBSave;
		BigDecimal totalBetforDBSave = BigDecimal.ZERO;
		BigDecimal betIncome = new BigDecimal(data.getBet());
		
		RealTimeAnalysisModel realTimeAnalysisModel = new RealTimeAnalysisModel();
		realTimeAnalysisModel.setPlatformType(platformType);
		realTimeAnalysisModel.setGameNameWithHallType(gameNameWithHallType);
		
		//更新玩家最後一次下注
		updateLastBet(data);
		
		//如果是立即玩
		if(data.isTrialAccount() && !data.isOfflineSpin()){
			totalBetforDBSave = totalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.NO_ACCOUNT);			
			totalBetforDBSave = Helper.add(totalBetforDBSave, betIncome);
			totalBetMap.get(platformType).get(gameNameWithHallType).put(MemberType.NO_ACCOUNT, totalBetforDBSave);
			memberTypeforDBSave = MemberType.NO_ACCOUNT;
			realTimeAnalysisModel.setTotalBetNoAccount(totalBetforDBSave);
		}
		//如果是體驗幣(只要立即玩是True就不會判斷進來)
		else if(data.isUseTrialCoin() && !data.isTrialAccount()){
			totalBetforDBSave = totalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.TRAIL_MEMBER);			
			totalBetforDBSave = Helper.add(totalBetforDBSave, betIncome);
			totalBetMap.get(platformType).get(gameNameWithHallType).put(MemberType.TRAIL_MEMBER, totalBetforDBSave);
			memberTypeforDBSave = MemberType.TRAIL_MEMBER;
			realTimeAnalysisModel.setTotalBetTrail(totalBetforDBSave);
		}
		//如果是離線SPIN
		else if(data.isOfflineSpin()){
			totalBetforDBSave = totalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.OFFLINE_SPIN_MEMBER);			
			totalBetforDBSave = Helper.add(totalBetforDBSave, betIncome);
			totalBetMap.get(platformType).get(gameNameWithHallType).put(MemberType.OFFLINE_SPIN_MEMBER, totalBetforDBSave);
			memberTypeforDBSave = MemberType.OFFLINE_SPIN_MEMBER;
			realTimeAnalysisModel.setTotalBetOfflineSpin(totalBetforDBSave);
		}
		//都不是上述，表示是一般進行遊戲的玩家
		else {
			totalBetforDBSave = totalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.FORMAL_MEMBER);			
			totalBetforDBSave = Helper.add(totalBetforDBSave, betIncome);
			totalBetMap.get(platformType).get(gameNameWithHallType).put(MemberType.FORMAL_MEMBER, totalBetforDBSave);
			memberTypeforDBSave = MemberType.FORMAL_MEMBER;
			realTimeAnalysisModel.setTotalBetFormal(totalBetforDBSave);
		}
		
		UpdateRealTimeTotalBetAsync saveData = new UpdateRealTimeTotalBetAsync();
		saveData.setModel(realTimeAnalysisModel);
		saveData.setMemberType(memberTypeforDBSave);
		saveData.setRealTimeBet(false);
		module.addAsyncCommand(saveData);
		
	}
	
	/**
	 * 更新realTimePlayerWithMachineMap裡面紀錄的lastBet
	 */
	public void updateLastBet(PlayerBetData data){
		//即時下注更新，直接用最新的一次下注額刷新
		try{
			PlayerSeatData seatData = realTimePlayerWithMachineMap.get(data.getGuid());
			
			seatData.setLastBet(new BigDecimal(data.getBet()));
			seatData.setLastBetTime(System.currentTimeMillis());
			
			realTimeAnalysisManager.updateBySeatData(RealTimeAnalysisManager.UPDATE, seatData);
		}
		catch(Exception e){
			if(log.isErrorEnabled()){
				log.error("can't find player in realTimePlayerWithMachineMap");
			}
			return;
		}
	}
	
	/**
	 * 把realTimePlayerWithMachineMap整份統計一次更新realTimeTotalBetMap
	 */
	public void saveRealTimePlayerWithMachineMapChangeBetResult(){
		HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>> tmpRealTimeTotalBetMap
		= new HashMap<PlatformType, HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>>();

		//把tmp建一個全部是0的完整MAP，以免下面GET不到
		for(PlatformType platformType : PlatformType.values()){
			tmpRealTimeTotalBetMap.put(platformType, new HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>());
			for(GameNameWithHallType gameNameWithHallType : GameNameWithHallType.values()){
				tmpRealTimeTotalBetMap.get(platformType).put(gameNameWithHallType, new HashMap<MemberType, BigDecimal>());
				for(MemberType memberType : MemberType.values()){
					tmpRealTimeTotalBetMap.get(platformType).get(gameNameWithHallType).put(memberType, BigDecimal.ZERO);
				}
			}
		}
		//把realTimePlayerWithMachineMap人數加進tmp
		for(String guidsString : realTimePlayerWithMachineMap.keySet()){
			PlayerSeatData tmpPlayerSeatData = realTimePlayerWithMachineMap.get(guidsString);
			//判斷BET期限是否過期
			if(tmpPlayerSeatData.isLastBetEffective()){
				BigDecimal count = tmpRealTimeTotalBetMap.get(tmpPlayerSeatData.getPlatformType()).get(tmpPlayerSeatData.getGameNameWithHallType()).get(tmpPlayerSeatData.getMemberType());
				count = Helper.add(count, tmpPlayerSeatData.getLastBet());
				tmpRealTimeTotalBetMap.get(tmpPlayerSeatData.getPlatformType()).get(tmpPlayerSeatData.getGameNameWithHallType()).put(tmpPlayerSeatData.getMemberType(), count);	
			}
		}
		//檢查整個tmpMAP和realTimeMemberCountMap有沒有不同
		for(PlatformType platformType : PlatformType.values()){
			for(GameNameWithHallType gameNameWithHallType : GameNameWithHallType.values()){
				for(MemberType memberType : MemberType.values()){
					//如果tmp與紀錄相左，就更新紀錄
					if(!tmpRealTimeTotalBetMap.get(platformType).get(gameNameWithHallType).get(memberType)
					    .equals(realTimeTotalBetMap.get(platformType).get(gameNameWithHallType).get(memberType))){
						realTimeTotalBetMap.get(platformType).get(gameNameWithHallType)
					    .put(memberType, tmpRealTimeTotalBetMap.get(platformType).get(gameNameWithHallType).get(memberType));
						
						RealTimeAnalysisModel model = new RealTimeAnalysisModel();
						model.setPlatformType(platformType);
						model.setGameNameWithHallType(gameNameWithHallType);
						
						switch (memberType) {
						case FORMAL_MEMBER:{
							model.setRealTimeBetFormal(realTimeTotalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.FORMAL_MEMBER));
							break;
						}
						case OFFLINE_SPIN_MEMBER:{
							model.setRealTimeBetOfflineSpin(realTimeTotalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.OFFLINE_SPIN_MEMBER));
							break;
						}
						case NO_ACCOUNT:{
							model.setRealTimeBetNoAccount(realTimeTotalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.NO_ACCOUNT));
							break;
						}
						case TRAIL_MEMBER:{
							model.setRealTimeBetTrial(realTimeTotalBetMap.get(platformType).get(gameNameWithHallType).get(MemberType.TRAIL_MEMBER));
							break;
						}
						default:
							break;
						}
						
						UpdateRealTimeTotalBetAsync saveData = new UpdateRealTimeTotalBetAsync();
						saveData.setRealTimeBet(true);
						saveData.setMemberType(memberType);
						saveData.setModel(model);
						module.addAsyncCommand(saveData);
						
					}
					else continue;
				}
			}
		}
	}

	/**
	 * 新版的更新下注
	 */
	public void saveRealTimeBet(){
		realTimeAnalysisManager.updateRealTimeBet(realTimePlayerWithMachineMap);
	}
	
	/**
	 * 初始化DB表，將DB裡面對照GameNameWithHallType缺少的紀錄建立起來
	 * 並且將realTimeMemberCountMap完整的建立起來且填入0
	 */
	public void InitRealTimeAnalysis() {		
		RealTimeAnalysisModel newOne = new RealTimeAnalysisModel();
		
		for(PlatformType platfromType : PlatformType.values()){					
			realTimeMemberCountMap.put(platfromType, new HashMap<GameNameWithHallType, HashMap<MemberType, Integer>>());
			totalBetMap.put(platfromType, new HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>());
			realTimeTotalBetMap.put(platfromType, new HashMap<GameNameWithHallType, HashMap<MemberType, BigDecimal>>());
			for (GameNameWithHallType gameNameWithHallTypetype : GameNameWithHallType.values()) {
				// 檢查有沒有遊戲館
				if (AnalysisDAO.getInstance().CheckRecordInTable(platfromType, gameNameWithHallTypetype) == false) {
					newOne.setGameNameWithHallType(gameNameWithHallTypetype);
					newOne.setPlatformType(platfromType);
					AnalysisDAO.getInstance().InsertNewRecord(newOne);
				}

				realTimeMemberCountMap.get(platfromType).put(gameNameWithHallTypetype, new HashMap<MemberType, Integer>());
				totalBetMap.get(platfromType).put(gameNameWithHallTypetype, new HashMap<MemberType, BigDecimal>());
				realTimeTotalBetMap.get(platfromType).put(gameNameWithHallTypetype, new HashMap<MemberType, BigDecimal>());
				for (MemberType mem : MemberType.values()) {
					realTimeMemberCountMap.get(platfromType).get(gameNameWithHallTypetype).put(mem, 0);
					totalBetMap.get(platfromType).get(gameNameWithHallTypetype).put(mem, BigDecimal.ZERO);
					realTimeTotalBetMap.get(platfromType).get(gameNameWithHallTypetype).put(mem, BigDecimal.ZERO);
				}
			}
		}
	}
	
	/**
	 * 開機時刷新DB目前即時統計<br>
	 * 主要是做<br>
	 * (1)初始化所有紀錄<br>
	 * (2暫時不做)從casino_game.player_keepseat拿出關機前離線掛機人數更新到<br>
	 * realTimeMemberCountMap與realTimePlayerWithMachineMap並寫DB<br>
	 */
	public void OnStartUpRefreshRealTimeAnalysis(){
		for(PlatformType platformType : PlatformType.values()){
			for(GameNameWithHallType gameNameWithHallType : GameNameWithHallType.values()){
				InitRealTimeAnalysisAsync async = new InitRealTimeAnalysisAsync();
				async.setPlatformType(platformType);
				async.setGameNameWithHallType(gameNameWithHallType);
				module.addAsyncCommand(async);
			}
		}
//		realTimePlayerWithMachineMap = PlayerKeepSeatDAO.getOfflineSpinPlayerSeatData();
//		saveRealTimePlayerWithMachineMapChangeResult(true);
	}
	
	/**
	 * 收到驗證過後的ClientReport就直接寫DB
	 * @param data
	 */
	public void recieveClientReport(ClientReportData data){		
		ClientReportModel clientReportModel = new ClientReportModel();
		clientReportModel.setClientReportContent(data.getReportContent());
		clientReportModel.setRecordTime(GameUtility.getCurrentDate());
		
		SaveClientReportAsync saveData = new SaveClientReportAsync();
		saveData.setClientReportModel(clientReportModel);
		module.addAsyncCommand(saveData);
	}
	
	/**
	 * 收到GMTOOL請求獲得即時玩家列表
	 */
	public HashMap<String, HashMap<String, ArrayList<String>>> getRealTimePlayerListInGame(SGmtoolGetRealTimePlayerListData data){
		PlatformType platformType = null;
		GameNameWithHallType gameNameWithHallType = null;
		if(data.getPlatformType() != null)
			platformType = PlatformType.valueOf(data.getPlatformType());
		if(data.getGameName() != null && data.getHallType() != null)
			gameNameWithHallType = GameNameWithHallType.getENUMbyString(data.getGameName(), data.getHallType());
		
		//取得當前的玩家與機台MAP
		HashMap<String, PlayerSeatData> tmpRealTimePlayerWithMachineMap = new HashMap(getInstance().getRealTimePlayerWithMachineMap());
		
		//回傳的MAP
		HashMap<String, HashMap<String, ArrayList<String>>> resultMap = new HashMap<String, HashMap<String,ArrayList<String>>>();
		
		for(Map.Entry<String, PlayerSeatData> entry : tmpRealTimePlayerWithMachineMap.entrySet()){
			if(entry != null){
				String playerGUID = entry.getKey();
				String pType = entry.getValue().getPlatformType().name();
				String gType = entry.getValue().getGameNameWithHallType().name();
				
				//如果有指定平台，且此玩家的平台不是指定的就跳下一位
				if(platformType != null){
					if(!pType.equals(platformType.name()))
						continue;
				}
				//如果有指定遊戲，且此玩家的遊戲不是指定的就跳下一位
				if(gameNameWithHallType != null){
					if(!gType.equals(gameNameWithHallType.name()))
						continue;
				}
			
				//如果平台都沒有表示裡面的遊戲類型一定也沒有
				if(resultMap.get(pType) == null){
					resultMap.put(pType, new HashMap<String, ArrayList<String>>());
					resultMap.get(pType).put(gType, new ArrayList<String>());
					resultMap.get(pType).get(gType).add(playerGUID);
				}
				//如果有平台
				else{
					//但是沒有遊戲類型
					if(resultMap.get(pType).get(gType) == null){
						resultMap.get(pType).put(gType, new ArrayList<String>());
						resultMap.get(pType).get(gType).add(playerGUID);
					}
					//有遊戲類型
					else{
						resultMap.get(pType).get(gType).add(playerGUID);
					}
				}
			}
		}
		return resultMap;
	}
	
	public void revRealTimePlayerCountRequest(SRealTimePlayerCountRequest request){
		ArrayList<String> destModules = request.getRequiredModuleIDs();
		if(destModules == null || destModules.size() == 0)
			return;
		
		HashMap<String, HashMap<String, Integer>> playerCount = new HashMap<String, HashMap<String,Integer>>();
		HashMap<String, PlayerSeatData> tmpMap = new HashMap<String, PlayerSeatData>(realTimePlayerWithMachineMap);
		
		for(Entry<String, PlayerSeatData> entry : tmpMap.entrySet()){
			PlayerSeatData seatData = entry.getValue();
			String gameNameWithHallType = seatData.getGameNameWithHallType().name();
			String gainRateStr = seatData.getGainRateStr();
			
			HashMap<String, Integer> playerMap = playerCount.get(gameNameWithHallType);
			if(playerMap == null){
				playerMap = new HashMap<String, Integer>();
				playerCount.put(gameNameWithHallType, playerMap);
			}
			
			if(playerMap.containsKey(gainRateStr)){
				int count = playerMap.get(gainRateStr) + 1;
				playerMap.put(gainRateStr, count);
			}
			else{
				playerMap.put(gainRateStr, 1);
			}
		}
		
		//把0的人數也補進來
		String oneGainRate = BigDecimal.ONE.setScale(Helper.VALUE_SCALE).toString();
		for(GameNameWithHallType gameHall : GameNameWithHallType.values()){
			String gameNameWithHallType = gameHall.name();
			String[] gameArray = gameNameWithHallType.split("_");
			String gameType = gameArray[0];
			String hallType = (gameArray.length >= 2) ? gameArray[1] : null;
			boolean isAffectGame = GainRateSystem.isAffectGame(gameType);
			
			if(!playerCount.containsKey(gameNameWithHallType)){
				HashMap<String, Integer> zeroMap = new HashMap<String, Integer>();
				ArrayList<GainRateData> gainRateDatas = GainRateSystem.getGainRateList(gameType, hallType);
				
				if(isAffectGame && gainRateDatas != null){
					for(GainRateData data : gainRateDatas){
						zeroMap.put(data.getGainRate().toString(), 0);
					}	
				}
				else{
					//不受幣值影響的遊戲，就給"1.0"放個0
					zeroMap.put(oneGainRate, 0);
				}
				
				playerCount.put(gameNameWithHallType, zeroMap);
			}
			else{
				
				HashMap<String, Integer> playerMap = playerCount.get(gameNameWithHallType);
				ArrayList<GainRateData> gainRateDatas = GainRateSystem.getGainRateList(gameType, hallType);
				//看看有沒有全部幣值的資訊都有
				if(isAffectGame && gainRateDatas != null){
					for(GainRateData data : gainRateDatas){
						String gainRateStr = data.getGainRate().toString();
						//補上沒有的
						if(!playerMap.containsKey(gainRateStr)){
							playerMap.put(gainRateStr, 0);
						}
					}
				}
			}
		}
		
		SRealTimePlayerCountResponse response = new SRealTimePlayerCountResponse();
		response.setPlayerCount(playerCount);
		
		for(String destModuleID : destModules){
			GroupMessage msg = new GroupMessage();
			msg.setDestModuleID(destModuleID);
			msg.setMessageType(MessageTypes.S_GET_REALTIME_PLAYER_COUNT_RESPONSE);
			msg.setMessageContent(response);
			
			try {
				MessageAction.sendMessage(msg);
			} catch (Exception e) {
				if(log.isErrorEnabled()){
					log.error("can't send to moduleID:"+destModuleID,e);
				}
				continue;
			}
			
		}

	}

	
	public void setModule(AnalysisModule module) {
		this.module = module;
	}

	
}